Skip to content

test(dev): live-host acceptance — installed proxy stays connected through rebuilds on Cursor, Claude, Codex (#179 stage 3) - #374

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
feat/179-s3-live-host-v2
Sep 3, 2026
Merged

test(dev): live-host acceptance — installed proxy stays connected through rebuilds on Cursor, Claude, Codex (#179 stage 3)#374
ScriptedAlchemy merged 1 commit into
mainfrom
feat/179-s3-live-host-v2

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Stage 3 of #179: the live-host acceptance proof for the stage-1 proxy (#339) and stage-2 install re-sync (#343), exercised through the exact installed host layout rather than a hand-built client.

  • New tests/dev-live-host.test.ts (integration, added to test:host-install): for each of cursor, claude, codex, startDevServer({ installHosts: [host] }) installs the dev variant into an isolated host home, the test reads the host's own installed MCP document, spawns that command over stdio, observes v1 from the echo tool, edits the MCP entry + Skill + hook sources, receives exactly one notifications/tools/list_changed on the same connection, observes v2, and verifies the installed marker advanced and the installed SKILL.md and hook module re-synced — with zero additional host-CLI plugin commands (a PATH-shim recorder around the real claude/codex binaries pins the count).
  • Opt-in AGENT_BUNDLE_HOST_INSTALL_CLAUDE_SESSION=1 lane: two real Claude model turns (--session-id then --resume) against the installed tree return v1:claude-session then v2:claude-session with no reinstall and the normal Claude settings/plugin tree byte-unchanged.
  • Codex has no inline plugin-loading surface for an isolated dev install; that lane is an honest it.skip with the reason in its title.
  • Support code lives in tests/support/host-install.ts (runDevLiveHostProof, runClaudeLiveDevSessionProof); no production code changes.

Evidence (local, rebased on c94df11ca)

  • pnpm build ✅ · pnpm typecheck ✅ · pnpm lint 0 errors / 0 warnings ✅
  • pnpm test:host-install 19 passed / 0 failed / 2 skipped (both skips are the documented opt-in / no-surface lanes) — real claude 2.1.257 and codex-cli 0.147.0 lanes executed.
  • AGENT_BUNDLE_HOST_INSTALL_CLAUDE_SESSION=1 run of dev-live-host.test.ts: 4 passed / 1 skipped — the real Claude model session observed v1:claude-sessionv2:claude-session through the installed plugin without reinstalling.
  • pnpm test:unit: 2664 passed; the 2 failures (mcp-probe-service timed-out-teardown timing, native-claude-contract 5 s timeout) reproduce identically on a clean origin/main checkout in this environment and are unrelated to this test-only diff.

Test plan

  • pnpm test:host-install with claude and codex on PATH
  • Opt-in Claude live-session lane
  • pnpm typecheck / pnpm lint
  • CI (gh pr checks --watch)

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: d13553e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T05:35:15.112585Z 070c680 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@374
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@374
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@374

commit: 070c680

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 070c680902

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +1657 to +1658
const settingsAndPluginsUnchanged = await normalClaudeSettingsAndPluginsUnchanged(
normalEnvironment,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Snapshot the same Claude home used by the subprocess

In the opt-in Claude session lane, runLiveHostScenario has already replaced process.env.HOME with the isolated scenario home, so this call's default homedir() snapshots that empty temporary home. The spawned claude process instead receives normalEnvironment, captured with the user's original HOME; consequently, changes to the user's real Claude settings or plugin tree are never detected even though the report claims they remained unchanged. Pass the original home explicitly to this guard.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed on main in #408 (a00273b): runClaudeLiveDevSessionProof captures homedir() before runLiveHostScenario swaps process.env.HOME for the isolated scenario home and passes { homeDirectory: normalHome } to normalClaudeSettingsAndPluginsUnchanged, so the guard digests the same real home the spawned claude turn runs against.

Comment on lines +1485 to +1489
let environment = isolatedEnvironment(options.environment, {
CLAUDE_CONFIG_DIR: roots.claudeConfig,
CODEX_HOME: roots.codexHome,
HOME: roots.home,
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Isolate Cursor's Windows profile before installing

On Windows, setting only HOME does not redirect os.homedir(), which resolves from USERPROFILE; startDevServer therefore installs the Cursor proof plugin under the developer's real .cursor/plugins/local directory while this test expects it under roots.home. A Windows developer with Cursor installed gets a failed test plus a persistent test plugin outside scenarioRoot, which the cleanup never removes. Override USERPROFILE as well or pass the isolated home directly to the install manager.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed on main in #408 (a00273b): isolatedEnvironment now sets USERPROFILE whenever it isolates HOME, so os.homedir() resolves to the scenario home on Windows too and the Cursor proof plugin lands under roots.home rather than the developer's real ~/.cursor/plugins/local. Applies to every isolated scenario in host-install.ts, not just the live lane.

Exercise exact installed dev proxy commands across epoch swaps and preserve honest opt-in evidence for real Claude and Codex sessions.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/179-s3-live-host-v2 branch from 070c680 to d13553e Compare September 3, 2026 05:56
@ScriptedAlchemy
ScriptedAlchemy merged commit 9fe8a35 into main Sep 3, 2026
4 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the feat/179-s3-live-host-v2 branch September 3, 2026 05:57
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
/#379/#385/#390) (#408)

* fix: address late review threads on merged PRs #368/#373/#374/#377/#378/#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)

* fix(dev): lease before publishing contract status; align fixtures with the portable byte lane

- EpochAdoptionPolicy acquires the epoch lease before publishing a passed
  dev.contract.status and announces adoption synchronously with it, so a
  status reader never sees "passed" for an epoch that is not yet adopted
- hooks.test: Codex PostToolUse accepts a string tool_response and rejects a
  missing one; Claude keeps the object check
- mcp-session-service/public-api-packed fixtures: Agent Plugins forbids
  placeholders in headers and non-bare/non-./ commands, and ordinary artifact
  validation now enforces the standard, so the fixtures carry a literal header
  and a bare `node` command

* chore(changeset): drop the Codex tool_response bullet already released by #404

* fix(playground,test): withdraw a failed catalog publication before releasing its staging link; bind custom observeProgress

- #persistSnapshot rolls the sidecar back while the staging link still exists
  when the post-link directory fsync fails, so a concurrent reader keeps seeing
  an in-progress publication until the path is withdrawn instead of adopting a
  briefly singly linked file
- contractProgressObserver invokes a client's observeProgress method with the
  client as receiver

* chore(changeset): one-paragraph summary ending with the PR reference

* fix(playground): recover a catalog staging link abandoned by an exited publisher

After the settle deadline, a matching .stage-<pid>-* link whose publisher pid
no longer exists is an abandoned publication of an already fsynced sidecar:
withdraw the orphan and adopt the sidecar instead of rejecting the epoch
forever. A live publisher's staging link is still never yanked. Adds the
@internal catalogStagingSettleDeadlineMs seam for deterministic tests.

* fix(playground): fsync the catalog directory after withdrawing an abandoned staging link

* fix(dev): forward request _meta (progress token) through McpSession and the dev matrix client

Lifecycle fixtures pass their generated progressToken as params._meta; the
session adapter and McpSession.callTool dropped it, so generated routes never
enabled sendProgress and every progress-gated lifecycle fixture failed the dev
matrix. McpSessionToolCallOptions and McpClient.callTool now carry _meta.

* fix(dev,playground): restart the adoption drain after a handoff race; keep the staging link when a sidecar rollback fails

- EpochAdoptionPolicy reschedules its drain from the completion handler when a
  candidate arrived between the loop's last empty check and #processing being
  cleared, and settled() waits through restarts
- #persistSnapshot releases the staging link after a failed publication only
  once the owned sidecar is confirmed withdrawn, so a rollback failure never
  leaves a singly linked sidecar for readers to adopt

* ci: retrigger checks for the rebased head

* chore: drop the portable byte-lane changes superseded by #406; keep the _meta assertions

* fix(playground): restore the staging guard when a recovery fsync fails

* fix(playground): accept a concurrently restored staging guard (EEXIST aliasing the sidecar) during recovery

* fix(dev): recheck supersession after the adoption lease settles

* fix(playground): keep a fresh pid-owned guard when recovery can neither re-link nor withdraw the sidecar

* fix(playground): fsync every compensating recovery guard before trusting it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant